/*
* Author: John Ryland (jryland), jryland@xiaofrog.com
* Company: InvertedLogic
*/
#define _DEBUG_MEMORY_OFF_ // Avoid recursion
#include <stdio.h>
#include <stdlib.h>
#include "Iterators.h"
#include "DebugMemory.h"
class AllocationRecord
{
public:
AllocationRecord() { ptr = 0; }
AllocationRecord(void *p, int s, const char *f, int l, int fl) {
ptr = p;
size = s;
file = f;
line = l;
flags = fl;
}
void *ptr;
int size;
const char *file;
int line;
int flags;
};
class IntRecord {
public:
IntRecord() { value = 0; }
IntRecord(const char *k, int x) { key = k; value = x; }
const char *key;
int value;
};
Dict<AllocationRecord> records;
Dict<IntRecord> fileLineTotals;
Dict<IntRecord> fileTotals;
int debug_mem_log_level = 0;
void DebugMemory::logMemoryAllocations(int level)
{
debug_mem_log_level = level;
}
#define normal_log if ( debug_mem_log_level > 0 ) printf
#define verbose_log if ( debug_mem_log_level > 1 ) printf
void DebugMemory::showMemoryLeaks()
{
#ifdef DEBUG
if ( records.count() == 0 )
printf("No leaks found.\n");
else
foreach (AllocationRecord rec, records)
printf("allocation from %s, line %i, leaked %i bytes at %p.\n", rec.file, rec.line, rec.size, rec.ptr);
#else
printf("Rebuild in DEBUG mode to enable memory logging.\n");
#endif
}
void DebugMemory::showMemoryAllocations()
{
#ifdef DEBUG
foreach (IntRecord rec, fileLineTotals)
printf("file and line %s allocated %i bytes\n", rec.key, rec.value);
foreach (IntRecord rec, fileTotals)
printf("file %s allocated %i bytes\n", rec.key, rec.value);
#else
printf("Rebuild in DEBUG mode to enable memory logging.\n");
#endif
}
const char *debug_mem_file = 0;
int debug_mem_line = 0;
void *DebugMemory::addLog(int type, void *p, unsigned int nNum, unsigned int size, const char *file, int line)
{
#ifdef DEBUG
const char *operation[] = { "-", "calloc", "valloc", "realloc", "reallocf", "malloc", "new", "new[]", "new", "new[]", "free", "delete", "delete[]" };
const int typeMap[] = { 0, 1, 1, 1, 1, 1, 2, 3, 2, 3, 1, 2, 3 };
char tmpLoc[1024];
const char *loc = tmpLoc;
if (!file) {
loc = "unknown location";
file = "unknown";
line = -1;
} else
snprintf(tmpLoc, 1024, "%s, line %i", file, line);
void *ptr = 0;
if ( type == 8 || type == 9 || type == 11 || type == 12 )
debug_mem_file = 0;
switch (type)
{
case 0: debug_mem_file = file; debug_mem_line = line; return 0;
case 1: ptr = calloc(nNum, size); size *= nNum; break;
case 2: ptr = valloc(size); break;
case 3: ptr = realloc(p, size); if (!ptr) return 0; break;
case 4: ptr = reallocf(p, size); break;
case 5: case 6: case 7: case 8:
case 9: ptr = malloc(size); break;
case 10: case 11: case 12: free(p); break;
default: return 0;
}
normal_log("Debug: doing %s ", operation[type]);
if ( p ) normal_log("of ptr %p ", p);
if ( size ) normal_log("of size %i ", size);
normal_log("from %s.\n", loc);
/*
if ( type == 10 || type == 11 || type == 12 ) {
normal_log("Debug: doing %s of ptr %p from %s.\n", operation[type], p, loc);
} else if ( type == 3 || type == 4 ) {
normal_log("Debug: doing %s of ptr %p of size %i from %s.\n", operation[type], p, size, loc);
} else
normal_log("Debug: doing %s of size %i from %s.\n", operation[type], size, loc);
*/
if ( type == 3 || type == 4 || type == 10 || type == 11 || type == 12 ) {
if (p) {
char key[32];
sprintf(key, "%p", p);
AllocationRecord rec = records.find(key);
//const AllocationRecord &rec = records.find(key);
if ( type == 3 || type == 4 )
normal_log("Debug: realloc'd memory was previously at %p and allocated %i bytes from %s, line %i\n", rec.ptr, rec.size, rec.file ? rec.file : "-", rec.line);
if ( rec.ptr ) {
if ( rec.flags != typeMap[type] ) {
printf("Unmatched allocation type for ptr %p from %s.\n", p, loc);
printf("eg Used free on pointer allocated with new, or malloc and delete\n");
printf("new[] and delete, or new and delete[], or new and realloc etc.\n");
records.remove(key); // Treat as warning and still proceed as if this could be okay.
} else {
records.remove(key);
verbose_log("removed allocation record for ptr %p\n", p);
}
} else {
printf("Delete/free of unmatched allocation for ptr %p from %s.\n", p, loc);
}
}
}
if ( type == 10 || type == 11 || type == 12 )
return 0;
/*
if ( type == 3 || type == 4 ) {
char key[32];
sprintf(key, "%p", p);
const AllocationRecord &rec = records.find(key);
normal_log("Debug: realloc'd memory was previously at %p and allocated %i bytes from %s, line %i\n", rec.ptr, rec.size, rec.file, rec.line);
}
*/
char key[32];
sprintf(key, "%p", ptr);
records.append(key, AllocationRecord(ptr, size, file, line, typeMap[type]));
verbose_log("added allocation record for ptr %p\n", ptr);
int x = fileLineTotals.find(loc).value;
if (x)
fileLineTotals.remove(loc);
fileLineTotals.append(loc, IntRecord(strdup(loc), x + size));
int y = fileTotals.find(file).value;
if (y)
fileTotals.remove(file);
fileTotals.append(file, IntRecord(strdup(file), y + size));
return ptr;
#endif
}
#ifdef DEBUG
void *operator new (size_t size) { return DebugMemory::addLog(8, 0, 0, size, debug_mem_file, debug_mem_line); }
void *operator new[] (size_t size) { return DebugMemory::addLog(9, 0, 0, size, debug_mem_file, debug_mem_line); }
void operator delete (void *p) { DebugMemory::addLog(11, p, 0, 0, debug_mem_file, debug_mem_line); }
void operator delete[](void *p) { DebugMemory::addLog(12, p, 0, 0, debug_mem_file, debug_mem_line); }
#endif